"""
binance 只允许一次获取 500bar，时间请求超过范围则只返回最新500条
"""
import datetime as dt
import datetime, time
from dateutil.tz import tzutc
import pandas as pd
from urllib.parse import urljoin
import requests
import json
from utils import read_sql, df_into_db


FREQUENCY_SHIFTING = {
    "1m": 30000,
    "5m": 150000,
    "15m": 450000,
    "30m": 900000,
    "1h": 1800000,
    "8h": 1800000,
    "1d": 14400000,
}
"""
QUANTAXIS 和 binance 的 frequency 常量映射关系
"""
Binance2FREQUENCY_DICT = {
    "1m": '1min',
    "5m": '5min',
    "15m": '15min',
    "30m": '30min',
    "1h": '60min',
    "8h": '8hour',
    "1d": 'day',
}
Binance_spot_base_url = "https://api.binance.com"
Binance_um_futures_base_url = 'https://fapi.binance.com'
Binance_cm_futures_base_url = 'https://dapi.binance.com'
Binance_options_base_url = 'https://vapi.binance.com'


def datetime_to_timestamp(date_=None):
    """
    返回当前UTC时间戳，输入的date_如果没有指定时区默认时区为北京时间

    :return: 类型 int
    """

    if (date_ is None):
        tz = dt.timezone(dt.timedelta(hours=8))
        date_ = dt.datetime.now(tz)

    # 方法1
    # original_datetime = datetime(1970, 1, 1, tzinfo=timezone.utc)
    # time_stamp = int((date_ - original_datetime).total_seconds())

    # 方法2
    time_stamp = int(dt.datetime.timestamp(date_))

    # 方法3
    # time_stamp = int(time.mktime(date_.timetuple()))  # 这种方式会丢掉时区信息, 默认为系统时区，不建议采用

    return time_stamp


def fetch_binance_funding_rate(symbol, market_type, start_time, end_time, frequency, callback_func=None):
    """
    Get the latest symbol‘s funding_rate data
    时间倒序切片获取算法，是各大交易所获取1min数据的神器，因为大部分交易所直接请求跨月跨年的1min分钟数据
    会直接返回空值，只有将 start_epoch，end_epoch 切片细分到 200/300 bar 以内，才能正确返回 kline，
    火币和binance，OKEx 均为如此，直接用跨年时间去直接请求上万bar 的 kline 数据永远只返回最近200条数据。
    """

    def format_funding_rate_data(datas, symbol, frequency):
        """
        # 归一化数据字段，转换填充必须字段，删除多余字段
        参数名 	类型 	描述
        time 	String 	开始时间
        open 	String 	开盘价格
        high 	String 	最高价格
        low 	String 	最低价格
        close 	String 	收盘价格
        volume 	String 	交易量
        """

        return frame

    datas = list()
    reqParams = {}
    reqParams['from'] = end_time - FREQUENCY_SHIFTING[frequency]
    if reqParams['from'] < start_time:
        reqParams['from'] = start_time
    reqParams['to'] = end_time

    while (reqParams['to'] > start_time):
        if ((reqParams['from'] > datetime_to_timestamp())) or ((reqParams['from'] > reqParams['to'])):
            # 出现“未来”时间，一般是默认时区设置，或者时间窗口滚动前移错误造成的
            # LOG.info(
            #     'A unexpected \'Future\' timestamp got, Please check self.missing_data_list_func param \'tzlocalize\' '
            #     'set. More info: {:s}@{:s} at {:s} but current time is {}'
            #         .format(
            #         symbol,
            #         frequency,
            #         timestamp_to_str(reqParams['from']),
            #         timestamp_to_str(datetime_to_timestamp())
            #     )
            # )
            # 跳到下一个时间段
            reqParams['to'] = int(reqParams['from'] - 1)
            reqParams['from'] = int(reqParams['from'] - FREQUENCY_SHIFTING[frequency])
            if reqParams['from'] < start_time:
                reqParams['from'] = start_time
            continue
        if market_type == 'um_futures':
            url = urljoin(Binance_um_futures_base_url, "/fapi/v1/fundingRate")
        else:
            url = urljoin(Binance_cm_futures_base_url, "/dapi/v1/fundingRate")
        params = {
            "symbol": symbol,
            "startTime": int(reqParams['from'] * 1000),
            "endTime": int(reqParams['to'] * 1000)
        }
        req = requests.get(url=url, params=params)
        funding_rate = json.loads(req.content)

        reqParams['to'] = int(reqParams['from'] - 1)
        reqParams['from'] = int(reqParams['from'] - FREQUENCY_SHIFTING[frequency])
        if reqParams['from'] < start_time:
            reqParams['from'] = start_time

        if (funding_rate is None):
            # 数据获取出错
            break
        if len(funding_rate) == 0:
            break
        if ((len(datas) > 0) and (funding_rate[-1]['fundingTime'] == datas[-1]['fundingTime'])):
            # 没有更多数据
            break

        datas.extend(funding_rate)

        if (callback_func is not None):
            frame = format_funding_rate_data(funding_rate, symbol, frequency)
            callback_func(frame, Binance2FREQUENCY_DICT[frequency])

    if len(datas) == 0:
        return None

    column_names = ['symbol', 'fundingTime', 'fundingRate']
    frame = pd.DataFrame(datas, columns=column_names)
    frame.rename({'fundingTime': 'timestamp', 'fundingRate': 'funding_rate'}, axis=1, inplace=True)
    frame.sort_values(by='timestamp', inplace=True)
    return frame

def get_exist_symbols(symbol):
    df = read_sql(f"select * from all_market_funding_rate where symbol = '{symbol}' order by datetime desc limit 1",
                  db_name="dataloader")
    return df


symbol = "FLMUSDT"
market_type = "um_futures"
start_date = '2019-01-01 00:00:00'
end_date = datetime.datetime.today().strftime('%Y-%m-%d')
start = time.mktime(datetime.datetime(int(start_date[:4]), int(start_date[5:7]), int(start_date[8:10]), 0, 0, 0, tzinfo=tzutc()).timetuple())
end = time.mktime(datetime.datetime(int(end_date[:4]), int(end_date[5:7]), int(end_date[-2:]), 23, 59, 59, tzinfo=tzutc()).timetuple())
timezone = '+0000'
data = fetch_binance_funding_rate(symbol, market_type, start, end, '8h')
data['timestamp'] = data.apply(lambda x: int(x['timestamp'] / 1000), axis=1)
data['datetime'] = pd.to_datetime(data['timestamp'], unit='s').dt.tz_localize('UTC').dt.tz_convert(timezone)
data['datetime'] = data['datetime'].dt.strftime('%Y-%m-%d %H:%M:%S')
symbol_df = get_exist_symbols(symbol)
max_datetime = symbol_df.iloc[0]["datetime"]
data = data[data["datetime"] >= max_datetime.strftime('%Y-%m-%d %H:%M:%S')]
data["type"] = symbol_df.iloc[0]['type']
assert float(data.iloc[0]["funding_rate"]) == symbol_df.iloc[0]["funding_rate"]
data = data[1:]
df_into_db(data, "dataloader", "all_market_funding_rate")
